package com.twb.core.security;

import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.twb.core.base.entity.BaseLoginUser;
import com.twb.core.cache.redis.RedisUtil;
import com.twb.core.exception.SystemException;
import com.twb.core.exception.TokenException;
import com.twb.core.security.jwt.Subject;
import com.twb.core.security.service.ITokenService;

/** 
 * @ClassName TwbSecurityUtil 
 * @Description 安全验证工具
 * @author DSY
 * @date 2018年11月3日 下午8:26:08 
 */ 
@Component
public class TwbSecurityUtil {

	@Autowired
	ITokenService twbJwtJedisTokenService;
	@Autowired
	TwbSecurityProperties securityProperties;
	@Autowired
	RedisUtil redisUtil;
	
	/**
	 * @Title  createToken
	 * @Description 创建token
	 * @param user
	 * @param request
	 * @return
	 * @throws Exception
	 * @author DSY
	 * @date 2018年11月2日 下午5:53:36
	 */
	public <T extends BaseLoginUser> String createToken(T user,HttpServletRequest request) {
		String appIdName = securityProperties.getAppIdName();
		String appTokenName = securityProperties.getAppTokenName();
		String appSecretName = securityProperties.getAppSecretName();
		
		String appId = request.getHeader(appIdName);
		String appToken = request.getHeader(appTokenName);
		String appSecret = request.getHeader(appSecretName);
		Long timeoutSeconds = securityProperties.getTimeOut().get(appToken);
		//此处可通过appToken和appSecret，校验该前端应用是否有权限访问服务端。具体实现方式可通过数据库或者配置文件。
		if(StringUtils.isEmpty(appId) || StringUtils.isEmpty(appToken) || StringUtils.isEmpty(appSecret)){
			throw new TokenException(appIdName+"/"+appTokenName+"/"+appSecretName+" must not be null");
		}
		
		if(null == timeoutSeconds) {
			timeoutSeconds = 3600L;//默认3600s
		}
		String token = null;
		try {
			Subject subject = new Subject(user.getId(), timeoutSeconds+"");
			token = twbJwtJedisTokenService.createToken(user, subject, appId, appToken, appSecret);
		} catch (Exception e) {
			throw new SystemException("token created failed "+e.getMessage());
		}
		return token;
	}
	
	/**
	 * @Title  checkAndRefreshToken
	 * @Class: TwbSecurityUtil.java
	 * @Description 验证token有效性，如果有效，则更新token的有效时间
	 * @param request
	 * @return
	 * @throws Exception
	 * @author DSY
	 * @date 2018年11月3日 下午6:40:43
	 */
	public boolean checkAndRefreshToken(HttpServletRequest request) throws Exception {
		String appIdName = securityProperties.getAppIdName();
		String appTokenName = securityProperties.getAppTokenName();
		String appSecretName = securityProperties.getAppSecretName();
		String tokenName = securityProperties.getTokenName();
		Boolean projectAuth = securityProperties.getProjectAuth();
		String appId = request.getHeader(appIdName);
		String appToken = request.getHeader(appTokenName);
		String appSecret = request.getHeader(appSecretName);
		String token = request.getHeader(tokenName);
		Long timeoutSeconds = securityProperties.getTimeOut().get(appToken);
		if(projectAuth) {
			//此处可通过appToken和appSecret，校验该前端应用是否有权限访问服务端。具体实现方式可通过数据库或者配置文件。
			if(StringUtils.isEmpty(appId) || StringUtils.isEmpty(appToken) || StringUtils.isEmpty(appSecret)){
				throw new TokenException(appIdName+"/"+appTokenName+"/"+appSecretName+" must not be null");
			}
		}
		if(null == timeoutSeconds) {
			timeoutSeconds = 3600L;//默认3600s
		}
		if (token == null) {
			String message = "token must not be null";
			throw new TokenException(message);
		} else {
			/*
			 * token验证通过后，刷新token的有效时间
			 */
			boolean checked = twbJwtJedisTokenService.checkToken(token, appToken, appSecret);
			if(!checked) {
				String message = String.format("token [%s] is invalid", token);
				throw new TokenException(message);
			}else {
				twbJwtJedisTokenService.refreshToken(token, timeoutSeconds);
			}
		}
		return true;
	}
	/**
	 * @Title  getLoginUser
	 * @Class: TwbSecurityUtil.java
	 * @Description 获取当前登录人
	 * @param request
	 * @return
	 * @author DSY
	 * @date 2018年11月3日 下午8:19:20
	 */
	public <T extends BaseLoginUser> T getLoginUser(HttpServletRequest request){
		String tokenName = securityProperties.getTokenName();
		try {
			String token = request.getHeader(tokenName);
			
			T loginUser = twbJwtJedisTokenService.getLoginUser(token);
			if(loginUser != null) {
				loginUser.setToken(token);
			}
			return loginUser;
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * @Title  removeLoingUser
	 * @Description 删除用户登录缓存
	 * @param token
	 * @author DSY
	 * @date 2018年11月27日 下午1:15:55
	 */
	public void removeLoginUser(String token) {
		twbJwtJedisTokenService.removeLoginUser(token);
	}
	
	/**
	 * @Title refreshLoginUser
	 * @Description 刷新登录用户信息
	 * @param user
	 * @param request
	 * @return
	 * @author DSY
	 * @date 2020年11月9日 下午3:54:04
	 */
	public <T extends BaseLoginUser> boolean refreshLoginUser(T user,HttpServletRequest request) {
		String appIdName = securityProperties.getAppIdName();
		String appTokenName = securityProperties.getAppTokenName();
		String appSecretName = securityProperties.getAppSecretName();
		String tokenName = securityProperties.getTokenName();
		Boolean projectAuth = securityProperties.getProjectAuth();
		String appId = request.getHeader(appIdName);
		String appToken = request.getHeader(appTokenName);
		String appSecret = request.getHeader(appSecretName);
		String token = request.getHeader(tokenName);
		Long timeoutSeconds = securityProperties.getTimeOut().get(appToken);
		if(projectAuth) {
			//此处可通过appToken和appSecret，校验该前端应用是否有权限访问服务端。具体实现方式可通过数据库或者配置文件。
			if(StringUtils.isEmpty(appId) || StringUtils.isEmpty(appToken) || StringUtils.isEmpty(appSecret)){
				throw new TokenException(appIdName+"/"+appTokenName+"/"+appSecretName+" must not be null");
			}
		}
		
		if(null == timeoutSeconds) {
			//默认3600s
			timeoutSeconds = 3600L;
		}
		Subject subject = new Subject(user.getId(), timeoutSeconds+"");
		Boolean result = twbJwtJedisTokenService.refreshLoginUser(user, token, subject);
		return  result;
	}
	
	/**
	 * @Title getAppId
	 * @Description 获取AppId
	 * @param request
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年3月29日 下午4:24:14
	 */
	public String getAppId(HttpServletRequest request) {
		String appIdName = securityProperties.getAppIdName();
		String appId = request.getHeader(appIdName);
		return appId;
	}
	/**
	 * @Title getAppToken
	 * @Description 获取AppToken
	 * @param request
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年3月29日 下午4:24:06
	 */
	public String getAppToken(HttpServletRequest request) {
		String appTokenName = securityProperties.getAppTokenName();
		String appToken = request.getHeader(appTokenName);
		return appToken;
	}
	/**
	 * @Title getAppSecret
	 * @Description 获取AppSecret
	 * @param request
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年3月29日 下午4:23:59
	 */
	public String getAppSecret(HttpServletRequest request) {
		String appSecretName = securityProperties.getAppSecretName();
		String appSecret = request.getHeader(appSecretName);
		return appSecret;
	}
	/**
	 * @Title getToken
	 * @Description 获取当前登录用户的token
	 * @param request
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年3月29日 下午4:23:50
	 */
	public String getToken(HttpServletRequest request) {
		String tokenName = securityProperties.getTokenName();
		String token = request.getHeader(tokenName);
		return token;
	}
	/**
	 * @Title getTimeoutSeconds
	 * @Description 获取超时时间
	 * @param request
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年3月29日 下午4:23:40
	 */
	public Long getTimeoutSeconds(HttpServletRequest request) {
		String appTokenName = securityProperties.getAppTokenName();
		String appToken = request.getHeader(appTokenName);
		Long timeoutSeconds = securityProperties.getTimeOut().get(appToken);
		if(null == timeoutSeconds) {
			//默认3600s
			timeoutSeconds = 3600L;
		}
		return timeoutSeconds;
	}
	
	/**
	 * @Title getLoginUserDataScopes
	 * @Description 获取登录用户自定义数据权限
	 * @param token
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年7月26日 上午9:15:26
	 */
	public Set<String> getLoginUserDataScopes(String token) {
		return twbJwtJedisTokenService.getLoginUserDataScopeCache(token);
	}

	/**
	 * @Title listOnlineLoginUser
	 * @Description 获取在线登录用户
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年7月26日 下午9:19:16
	 */
	public List<BaseLoginUser> searchOnlineLoginUser(int pageNum,int pageSize) {
		// TODO Auto-generated method stub
		return twbJwtJedisTokenService.searchOnlineLoginUser(pageNum, pageSize);
	}
	
	/**
	 * @Title countOnlineLoginUser
	 * @Description 获取在线登录用户数量
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年11月1日 下午9:01:32
	 */
	public Long countOnlineLoginUser(){
		return twbJwtJedisTokenService.countOnlineLoginUser();
	}
	/**
	 * @Title getLoginUser
	 * @Description 获取登录用户信息
	 * @param token
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年7月26日 下午9:28:15
	 */
	public <T extends BaseLoginUser> T getLoginUser(String token){
		try {
			T loginUser = twbJwtJedisTokenService.getLoginUser(token);
			return loginUser;
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * @Title getTwbLoginUser
	 * @Description 获取TwbLoginUser用户信息，包括组织机构、角色、权限等信息
	 * @param token
	 * @return
	 * @AuthorOriginally DSY
	 * @date 2022年8月18日 上午11:03:40
	 */
	public <T extends BaseLoginUser> T getTwbLoginUser(String token){
		try {
			T loginUser = twbJwtJedisTokenService.getTwbLoginUser(token);
			return loginUser;
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
     * @Title lockLoginUser
     * @Description 根据账号锁定用户信息
     * @param loginName 用户名
     * @param timeout 锁定时间
     * @param maxCount 最大登录次数
     * @return
     * @AuthorOriginally 杜顺亚
     * @date 2022年10月28日 下午6:35:41
     */
	public boolean lockLoginUser(String loginName,long timeout,int maxCount) {
		return twbJwtJedisTokenService.lockLoginUser(loginName, timeout, maxCount);
	}
    
    /**
     * @Title unlockLoginUser
     * @Description 解锁账户信息
     * @param loginName
     * @return
     * @AuthorOriginally 杜顺亚
     * @date 2022年10月28日 下午6:36:49
     */
	public boolean unlockLoginUser(String loginName) {
		return twbJwtJedisTokenService.unlockLoginUser(loginName);
	}
	
	/**
	 * @Title checklockLoginUser
	 * @Description 检查账号是否被锁
	 * @param loginName
	 * @return
	 * @AuthorOriginally 杜顺亚
	 * @date 2022年10月28日 下午7:19:36
	 */
	public boolean checklockLoginUser(String loginName,int maxCount) {
		return twbJwtJedisTokenService.checklockLoginUser(loginName,maxCount);
	}
}
